package com.tpsix.dto;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.db.Db;
import cn.hutool.db.Entity;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.handlers.AbstractSqlParserHandler;
import com.tpsix.domain.SysRole;
import com.tpsix.enums.DataScopeTypeEnum;
import com.tpsix.mapper.SysRoleMapper;
import com.tpsix.security.SecurityUserDetails;
import com.tpsix.service.SysRoleService;
import com.tpsix.util.SecurityUtils;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;
import java.sql.Connection;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 数据权限拦截器
 */
@Order(90)
@Slf4j
@Component
@AllArgsConstructor
@Intercepts({
        @Signature(type = StatementHandler.class, method = "prepare", args = {
                Connection.class,
                Integer.class
        })
})
public class DataScopeInterceptor extends AbstractSqlParserHandler implements Interceptor {

    private static final String DELEGATE_MAPPED_STATEMENT = "delegate.mappedStatement";

    private static final String DELEGATE_BOUND_SQL = "delegate.boundSql";
    private static final String DELEGATE_BOUND_SQL_VALUE = "delegate.boundSql.sql";

    private static final String NESTED_SQL = "SELECT * FROM ( %s ) temp_data_scope WHERE temp_data_scope.%s IN (%s)";

    private final DataSource dataSource;

    @Override
    public Object intercept(Invocation invocation) throws Throwable {

        StatementHandler statementHandler = PluginUtils.realTarget(invocation.getTarget());
        MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
        this.sqlParser(metaObject);

        // 只拦截查询
        MappedStatement mappedStatement = (MappedStatement) metaObject.getValue(DELEGATE_MAPPED_STATEMENT);
        if (!SqlCommandType.SELECT.equals(mappedStatement.getSqlCommandType())) {
            return invocation.proceed();
        }

        BoundSql boundSql = (BoundSql) metaObject.getValue(DELEGATE_BOUND_SQL);

        // 要执行的SQL语句
        String executeSql = boundSql.getSql();
        // 参数
        Object parameter = boundSql.getParameterObject();

        // 在参数中查找DataScope参数
        DataScope dataScope = findDataScopeObject(parameter);
        if (Objects.isNull(dataScope)) {
            return invocation.proceed();
        }

        String scopeName = dataScope.getScopeName();

        // 部门 id 范围
        List<Integer> deptIds = dataScope.getDeptIds();

        if (CollectionUtils.isEmpty(deptIds)) {
            SecurityUserDetails userDetails = SecurityUtils.getUser();
            if (Objects.isNull(userDetails)) {
                throw new Exception("用户未登录");
            }

            // 获取用户角色 code
            List<String> userRoleCodeList = userDetails.getAuthorities()
                    .stream()
                    .map(GrantedAuthority::getAuthority)
                    .filter(authority -> authority.startsWith("ROLE_"))
                    .collect(Collectors.toList());

            String sql = "SELECT * FROM sys_role WHERE role_code in ( '" + CollectionUtil.join(userRoleCodeList, ",") + "')";

            // 查询角色数据范围
            Optional<Entity> query = Db.use(dataSource).query(sql).stream().min(Comparator.comparing(v -> v.getInt("ds_type")));

            if (query.isPresent()) {
                Entity queryVal = query.get();

                // 数据范围
                int dsType = queryVal.getInt("ds_type");

                // 查询全部
                if (DataScopeTypeEnum.ALL.getType() == dsType) {
                    return invocation.proceed();
                }

                // 获取自定义、本级、本级及下级
                String dsScope = queryVal.getStr("ds_scope");

                List<Integer> dsScopeIds = Stream.of(StringUtils.split(dsScope, ",")).map(Integer::valueOf).collect(Collectors.toList());

                deptIds.addAll(dsScopeIds);

                String deptIdsJoin = deptIds.stream().map(String::valueOf).collect(Collectors.joining(","));

                executeSql = String.format(NESTED_SQL, executeSql, scopeName, deptIdsJoin);

                metaObject.setValue(DELEGATE_BOUND_SQL_VALUE, executeSql);

                return invocation.proceed();
            }
        }
        return invocation.proceed();
    }

    /**
     * 查找参数是否包括 DataScope 对象
     *
     * @param parameterObj 参数列表
     * @return DataScope
     */
    private DataScope findDataScopeObject(Object parameterObj) {
        if (parameterObj instanceof DataScope) {
            return (DataScope) parameterObj;
        } else if (parameterObj instanceof Map) {
            for (Object val : ((Map<?, ?>) parameterObj).values()) {
                if (val instanceof DataScope) {
                    return (DataScope) val;
                }
            }
        }
        return null;
    }
}
